/*
 * Copyright (c) 2016, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

//*****************************************************************************
//
//! \addtogroup cloud_ota
//! @{
//
//*****************************************************************************

//*****************************************************************************
//
//    Application Name     -   sample OTA process
//    Application Overview -   Runs the OTA process within an application
//    Application Details  -   Refer to 'Cloud OTA' README.html
//
//*****************************************************************************

/* TI-DRIVERS Header files */
#include <ti/drivers/SPI.h>
#include <ti/display/Display.h>

/* Simplelink includes */
#include <ti/drivers/net/wifi/simplelink.h>

/* OTA library includes */
//#include <ti/net/ota/ota.h>
//#include <ti/net/ota/otauser.h>
#include "ota.h"
#include "otauser.h"
/* Application Header files */
#include "wifi_doorlock_app.h"
#include "cloud_ota_task.h"
#include "platform.h"
#include "mqueue.h"

#define OTA_EVT_TIMEOUT     1000 // 1000 ms = 1 sec

/*
 *  \brief  Application state's context
 */
typedef struct
{
    OtaState              currentState;       /* Current state of the application */
    volatile uint32_t       pendingEvents;      /* Events pending to be processed */
    uint32_t                asyncEvtTimeout;    /* Timeout value*/
}OtaContext;

/*!
 *  \brief  Entry in the lookup table
 */
typedef struct
{
    fptr_EventHandler   p_evtHndl;  /* Pointer to the event handler */
    OtaState          nextState;  /* Next state of the application */

}s_OtaTblEntry;

/*!
 *  \brief  Application state's context
 */
OtaContext gOtaCtx;

/* Overall application control block */
extern Application_CB App_CB;
extern Display_Handle display;

/* local ota data */
OTA_memBlock otaMemBlock;
Ota_optServerInfo g_otaOptServerInfo; /* must be global, will be pointed by extlib_ota */

uint8_t  g_StopInProgress       = 0;
uint8_t  g_performOtaCommand    = 0;

int32_t OtaCount_Done           = 0;
int32_t OtaCount_Warnings       = 0;
int32_t OtaCount_Errors         = 0;

/* The message q */
mqd_t g_OtaSMQueue;
timer_t gOtaAsyncEventTimer;

/*!
 *  \brief  This function reports the error condition by displaying the result
 *          on console o/p
 *  \param  None
 *  \return 0 on success, -ve otherwise
 */
static int32_t ReportError();
static int32_t StartAsyncEvtTimer();
static int32_t StopAsyncEvtTimer();

/*!
 *  \brief  OTA access funtions
 *  \param  None
 *  \return 0 on success, -ve otherwise
 */
int32_t OtaInit();
int32_t OtaCheckAndDoCommit();
int32_t ProcessRestartMcu();
int32_t OtaImageTestingAndReset();
int32_t OtaRunStep();
int16_t OtaSignalEvent(OtaEvent event);
void OtaAsyncEvtTimerIntHandler(sigval val);

/*!
 *  \brief   Application lookup/transition table
 */
const s_OtaTblEntry gTransitionTable[OTA_STATE_MAX][OTA_EVENT_MAX] =
{
     /* OTA_STATE_WAIT_FOR_CONNECTION */
     {
         /* OTA_EVENT_NULL                      */    {NULL                     , OTA_STATE_WAIT_FOR_CONNECTION     },
         /* OTA_EVENT_START                     */    {NULL                     , OTA_STATE_WAIT_FOR_CONNECTION     }, /* OTA - Init OTA after bundle commit */

         /* OTA_EVENT_CONNECTED                 */    {NULL                     , OTA_STATE_WAIT_FOR_IP             }, /* OTA - Response to connect WLAN event */
         /* OTA_EVENT_DISCONNECT                */    {NULL                     , OTA_STATE_WAIT_FOR_CONNECTION     }, /* OTA - Response to disconnect WLAN event */
         /* OTA_EVENT_IP_ACQUIRED               */    {NULL                     , OTA_STATE_WAIT_FOR_CONNECTION     }, /* OTA - Response to IP acquired WLAN event */

         /* OTA_EVENT_CONTINUE                  */    {NULL                     , OTA_STATE_ERROR                   },
         /* OTA_EVENT_CHECK_DONE                */    {ReportError              , OTA_STATE_ERROR                   },
         /* OTA_EVENT_DOWNLOAD_DONE             */    {ReportError              , OTA_STATE_ERROR                   },

         /* OTA_EVENT_ERROR                     */    {ReportError              , OTA_STATE_ERROR                   },
         /* OTA_EVENT_TIMEOUT                   */    {ReportError              , OTA_STATE_WAIT_FOR_CONNECTION     },
         /* OTA_EVENT_RESTART                   */    {ProcessRestartMcu        , OTA_STATE_WAIT_FOR_CONNECTION     }
     },

     /* OTA_STATE_WAIT_FOR_IP */
     {
          /* OTA_EVENT_NULL                      */    {NULL                     , OTA_STATE_WAIT_FOR_IP             },
          /* OTA_EVENT_START                     */    {NULL                     , OTA_STATE_WAIT_FOR_IP             }, /* OTA - Init OTA after bundle commit */

          /* OTA_EVENT_CONNECTED                 */    {NULL                     , OTA_STATE_WAIT_FOR_IP             }, /* OTA - Response to connect WLAN event */
          /* OTA_EVENT_DISCONNECT                */    {NULL                     , OTA_STATE_WAIT_FOR_CONNECTION     }, /* OTA - Response to disconnect WLAN event */
          /* OTA_EVENT_IP_ACQUIRED               */    {StartAsyncEvtTimer       , OTA_STATE_IDLE                    }, /* OTA - Response to IP acquired WLAN event */

          /* OTA_EVENT_CONTINUE                  */    {ReportError              , OTA_STATE_ERROR                   },
          /* OTA_EVENT_CHECK_DONE                */    {ReportError              , OTA_STATE_ERROR                   },
          /* OTA_EVENT_DOWNLOAD_DONE             */    {ReportError              , OTA_STATE_ERROR                   },

          /* OTA_EVENT_ERROR                     */    {ReportError              , OTA_STATE_ERROR                   },
          /* OTA_EVENT_TIMEOUT                   */    {ProcessRestartMcu        , OTA_STATE_WAIT_FOR_IP             },
          /* OTA_EVENT_RESTART                   */    {ProcessRestartMcu        , OTA_STATE_WAIT_FOR_CONNECTION     }
     },


    /* OTA_STATE_IDLE */
    {
         /* OTA_EVENT_NULL                       */    {NULL                     , OTA_STATE_IDLE                    },
         /* OTA_EVENT_START                      */    {OtaInit                  , OTA_STATE_RUN                     }, /* OTA - Init OTA after bundle commit */

         /* OTA_EVENT_CONNECTED                  */    {NULL                     , OTA_STATE_WAIT_FOR_IP             }, /* OTA - Response to connect WLAN event */
         /* OTA_EVENT_DISCONNECT                 */    {NULL                     , OTA_STATE_WAIT_FOR_CONNECTION     }, /* OTA - Response to disconnect WLAN event */
         /* OTA_EVENT_IP_ACQUIRED                */    {OtaCheckAndDoCommit      , OTA_STATE_IDLE                    }, /* OTA - Response to IP acquired WLAN event */

         /* OTA_EVENT_CONTINUE                   */    {ReportError              , OTA_STATE_ERROR                   },
         /* OTA_EVENT_CHECK_DONE                 */    {ReportError              , OTA_STATE_ERROR                   },
         /* OTA_EVENT_DOWNLOAD_DONE              */    {ReportError              , OTA_STATE_ERROR                   },

         /* OTA_EVENT_ERROR                      */    {ReportError              , OTA_STATE_ERROR                   },
         /* OTA_EVENT_TIMEOUT                    */    {OtaCheckAndDoCommit      , OTA_STATE_IDLE                    },
         /* OTA_EVENT_RESTART                    */    {ProcessRestartMcu        , OTA_STATE_WAIT_FOR_CONNECTION     }
    },


    /* OTA_STATE_RUN */
    {
        /* OTA_EVENT_NULL                       */    {NULL                     , OTA_STATE_RUN                     },
        /* OTA_EVENT_START                      */    {ReportError              , OTA_STATE_ERROR                   },

        /* OTA_EVENT_CONNECTED                  */    {ReportError              , OTA_STATE_WAIT_FOR_IP             }, /* OTA - Response to connect WLAN event */
        /* OTA_EVENT_DISCONNECT                 */    {ReportError              , OTA_STATE_WAIT_FOR_CONNECTION     }, /* OTA - Response to disconnect WLAN event */
        /* OTA_EVENT_IP_ACQUIRED                */    {ReportError              , OTA_STATE_IDLE                    }, /* OTA - Response to IP acquired WLAN event */

        /* OTA_EVENT_CONTINUE                   */    {OtaRunStep               , OTA_STATE_RUN                     }, /* OTA - run OTA steps untill download done */
        /* OTA_EVENT_CHECK_DONE                 */    {NULL                     , OTA_STATE_IDLE                    }, /* OTA - back to pinging */
        /* OTA_EVENT_DOWNLOAD_DONE              */    {OtaImageTestingAndReset  , OTA_STATE_IDLE                    }, /* OTA - move bundle to testing mode and reset the MCU/NWP and restart the SM */

        /* OTA_EVENT_ERROR                      */    {ReportError              , OTA_STATE_ERROR                   }, /* OTA - on ota error (security alert, max consecutive retries, ...)  - stop */
        /* OTA_EVENT_TIMEOUT                    */    {ReportError              , OTA_STATE_ERROR                   },
        /* OTA_EVENT_RESTART                    */    {ProcessRestartMcu        , OTA_STATE_WAIT_FOR_CONNECTION     }
    },

    /* OTA_STATE_ERROR */
    {
        /* OTA_EVENT_NULL                       */    {ReportError              , OTA_STATE_ERROR                   },
        /* OTA_EVENT_START                      */    {ReportError              , OTA_STATE_ERROR                   },

        /* OTA_EVENT_CONNECTED                  */    {ReportError              , OTA_STATE_ERROR                   }, /* OTA - Response to connect WLAN event */
        /* OTA_EVENT_DISCONNECT                 */    {ReportError              , OTA_STATE_ERROR                   }, /* OTA - Response to disconnect WLAN event */
        /* OTA_EVENT_IP_ACQUIRED                */    {ReportError              , OTA_STATE_ERROR                   }, /* OTA - Response to IP acquired WLAN event */

        /* OTA_EVENT_CONTINUE                   */    {ReportError              , OTA_STATE_ERROR                   },
        /* OTA_EVENT_CHECK_DONE                 */    {ReportError              , OTA_STATE_ERROR                   },
        /* OTA_EVENT_DOWNLOAD_DONE              */    {ReportError              , OTA_STATE_ERROR                   },

        /* OTA_EVENT_ERROR                      */    {ReportError              , OTA_STATE_ERROR                   },
        /* OTA_EVENT_TIMEOUT                    */    {ReportError              , OTA_STATE_ERROR                   },
        /* OTA_EVENT_RESTART                    */    {ReportError              , OTA_STATE_ERROR                   }
    }
};

int32_t OtaInit()
{
    int16_t Status;

    StopAsyncEvtTimer();

    /* Configure the commit watchdog timer in seconds */
    Status = Platform_CommitWdtConfig(50);
    if (Status < 0)
    {
        Display_printf(
                display,
                0,
                0,
                "[OTA Task] OtaInit: ERROR from Platform_CommitWdtConfig. Status=%d",
                Status);
        OtaSignalEvent(OTA_EVENT_ERROR);
        return Status;
    }

    Display_printf(display, 0, 0, "[OTA Task] OtaInit: statistics = %d, %d, %d",
                   OtaCount_Done, OtaCount_Warnings, OtaCount_Errors);
    /* init OTA */
    Display_printf(display, 0, 0, "[OTA Task] OtaInit: call Ota_init");
    Status = OTA_init(OTA_RUN_NON_BLOCKING, &otaMemBlock, NULL);
    if (Status < 0)
    {
        Display_printf(display, 0, 0,
                       "[OTA Task] OtaInit: ERROR from Ota_init. Status=%d",
                       Status);
        OtaSignalEvent(OTA_EVENT_ERROR); /* Fatal error */
        return Status;
    }

    /* set OTA server info */
    Display_printf(
            display,
            0,
            0,
            "[OTA Task] OtaConfig: call OTA_set EXTLIB_OTA_SET_OPT_SERVER_INFO, ServerName=%s",
            OTA_SERVER_NAME);
    g_otaOptServerInfo.IpAddress = OTA_SERVER_IP_ADDRESS;
    g_otaOptServerInfo.SecuredConnection = OTA_SERVER_SECURED;
    strcpy((char *) g_otaOptServerInfo.ServerName, OTA_SERVER_NAME);
    strcpy((char *) g_otaOptServerInfo.VendorToken, OTA_VENDOR_TOKEN);
    Status = OTA_set(EXTLIB_OTA_SET_OPT_SERVER_INFO, sizeof(g_otaOptServerInfo),
                     (uint8_t *) &g_otaOptServerInfo, 0);
    if (Status < 0)
    {
        Display_printf(
                display,
                0,
                0,
                "[OTA Task] OtaInit: ERROR from OTA_set EXTLIB_OTA_SET_OPT_SERVER_INFO. Status=%d",
                Status);
        OtaSignalEvent(OTA_EVENT_ERROR); /* Fatal error */
        return Status;
    }

    /* set vendor ID */
    Display_printf(
            display,
            0,
            0,
            "[OTA Task] OtaConfig: call OTA_set EXTLIB_OTA_SET_OPT_VENDOR_ID, VendorDir=%s",
            OTA_VENDOR_DIR);
    Status = OTA_set(EXTLIB_OTA_SET_OPT_VENDOR_ID, strlen(OTA_VENDOR_DIR),
                     (uint8_t *) OTA_VENDOR_DIR, 0);
    if (Status < 0)
    {
        Display_printf(
                display,
                0,
                0,
                "[OTA Task] OtaInit: ERROR from OTA_set EXTLIB_OTA_SET_OPT_VENDOR_ID. Status=%d",
                Status);
        OtaSignalEvent(OTA_EVENT_ERROR); /* Fatal error */
        return Status;
    }

    OtaSignalEvent(OTA_EVENT_CONTINUE);
    return Status;
}

int32_t OtaCheckAndDoCommit()
{
    int32_t isPendingCommit;
    int32_t isPendingCommit_len;
    int32_t Status;

    Display_printf(display, 0, 0, "[OTA Task] Checking status of OTA");

    /* At this stage we have fully connected to the network (IPv4_Acquired) */
    /* If the MCU image is under test, the ImageCommit process will commit the new image and might reset the MCU */
    Status = OTA_get(EXTLIB_OTA_GET_OPT_IS_PENDING_COMMIT,
                     (int32_t *) &isPendingCommit_len,
                     (uint8_t *) &isPendingCommit);
    if (Status < 0)
    {
        Display_printf(
                display,
                0,
                0,
                "[OTA Task] OtaCheckDoCommit: OTA_get ERROR on EXTLIB_OTA_GET_OPT_IS_PENDING_COMMIT, Status = %d",
                Status);
        OtaSignalEvent(OTA_EVENT_ERROR); /* Fatal error */
        return 0; /* action in state machine - return 0 in order to process the OTA_EVENT_ERROR */
    }

    Display_printf(display, 0, 0, "[OTA Task] isPendingCommit = %d",
                   isPendingCommit);

    /* commit now because 1. the state is PENDING_COMMIT 2. there was successful wlan connection */
    if (isPendingCommit)
    {
        Status = OTA_set(EXTLIB_OTA_SET_OPT_IMAGE_COMMIT, 0, NULL, 0);
        if (Status < 0)
        {
            Display_printf(
                    display,
                    0,
                    0,
                    "[OTA Task] OtaCheckDoCommit: OTA_set ERROR on EXTLIB_OTA_SET_OPT_IMAGE_COMMIT, Status = %d",
                    Status);
            OtaSignalEvent(OTA_EVENT_ERROR); /* Can be in wrong state, ToDo - no error */
            return 0; /* action in state machine - return 0 in order to process the OTA_EVENT_ERROR */
        }
        Display_printf(
                display,
                0,
                0,
                "[OTA Task] OtaCheckDoCommit: OTA success, new image committed and currently running");
        /* Stop the commit WDT */
        Platform_CommitWdtStop();
    }
    return 0;
}

int32_t ProcessRestartMcu()
{
    int32_t retVal = 0;
    struct controlMsg controlMsg;

    Display_printf(display, 0, 0, "\n\n");
    Display_printf(display, 0, 0,
                   "[OTA Task] ProcessRestartMcu: reset the platform...");

    if (App_CB.controlQueue != NULL)
    {
        controlMsg.threadID = 2;
        controlMsg.msg = CONTROL_MSG_GENERAL_RESET;
        mq_send(App_CB.controlQueue, (char *) &controlMsg,
                sizeof(struct controlMsg), 0);
    }
    else
    {
        retVal = -1;
    }
    return retVal;
}

int32_t OtaRunStep()
{
    int32_t Status;
    Ota_optVersionsInfo VersionsInfo;
    int32_t Optionlen;

    Status = OTA_run();
    switch (Status)
    {
    case OTA_RUN_STATUS_CONTINUE:
        /* continue calling Ota_run */
        OtaSignalEvent(OTA_EVENT_CONTINUE);
        break;

    case OTA_RUN_STATUS_CONTINUE_WARNING_FAILED_CONNECT_OTA_SERVER:
    case OTA_RUN_STATUS_CONTINUE_WARNING_FAILED_RECV_APPEND:
    case OTA_RUN_STATUS_CONTINUE_WARNING_FAILED_REQ_OTA_DIR:
    case OTA_RUN_STATUS_CONTINUE_WARNING_FAILED_REQ_FILE_URL:
    case OTA_RUN_STATUS_CONTINUE_WARNING_FAILED_CONNECT_FILE_SERVER:
    case OTA_RUN_STATUS_CONTINUE_WARNING_FAILED_REQ_FILE_CONTENT:
    case OTA_RUN_STATUS_CONTINUE_WARNING_FAILED_FILE_HDR:
    case OTA_RUN_STATUS_CONTINUE_WARNING_FAILED_DOWNLOAD_AND_SAVE:
        Display_printf(
                display,
                0,
                0,
                "[OTA Task] OtaRunStep: WARNING Ota_run, Status=%d, continue for next OTA retry",
                Status);
        OtaCount_Warnings++;
        /* on warning, continue calling Ota_run for next retry */
        OtaSignalEvent(OTA_EVENT_CONTINUE);
        break;

    case OTA_RUN_STATUS_NO_UPDATES:
        /* OTA will go back to IDLE and next Ota_run will restart the process */
        Display_printf(
                display, 0, 0,
                "[OTA Task] OtaRunStep: status from Ota_run: no updates");
        OtaSignalEvent(OTA_EVENT_CHECK_DONE);
        break;

    case OTA_RUN_STATUS_CHECK_NEWER_VERSION:
        /* OTA find new version - in compare to ota.dat file version */
        /* host should decide if to use the new version or to ignore it */
        Display_printf(
                display,
                0,
                0,
                "[OTA Task] OtaRunStep: status from Ota_run: OTA_RUN_STATUS_CHECK_NEWER_VERSION, accept and continue");
        OTA_get(EXTLIB_OTA_GET_OPT_VERSIONS, (int32_t *) &Optionlen,
                (uint8_t *) &VersionsInfo);
        Display_printf(
                display,
                0,
                0,
                "[OTA Task] OtaRunStep: CurrentVersion=%s, NewVersion=%s, Start download ...",
                VersionsInfo.CurrentVersion, VersionsInfo.NewVersion);

        OTA_set(EXTLIB_OTA_SET_OPT_ACCEPT_UPDATE, 0, NULL, 0);
        OtaSignalEvent(OTA_EVENT_CONTINUE);
        break;

    case OTA_RUN_STATUS_CHECK_OLDER_VERSION:
        /* OTA find old version - in compare to ota.dat file version */
        /* host should decide if to use this old version or to ignore it */
        Optionlen = sizeof(Ota_optVersionsInfo);
        Display_printf(
                display,
                0,
                0,
                "[OTA Task] OtaRunStep: status from Ota_run: OTA_RUN_STATUS_CHECK_OLDER_VERSION");
#ifdef OTA_LOOP_TESTING
        /* just for loop testing  - ignore status OLDER_VERSION */
        Display_printf(display, 0, 0,"[OTA Task] OtaRunStep: ignore it just for loop testing");
        OTA_set(EXTLIB_OTA_SET_OPT_ACCEPT_UPDATE, 0, NULL, 0);
        OtaSignalEvent(OTA_EVENT_CONTINUE);
#else
        OTA_set(EXTLIB_OTA_SET_OPT_DECLINE_UPDATE, 0, NULL, 0);
        OtaSignalEvent(OTA_EVENT_CHECK_DONE);
#endif
        break;

    case OTA_RUN_STATUS_DOWNLOAD_DONE:
        Display_printf(
                display,
                0,
                0,
                "[OTA Task] OtaRunStep: status from Ota_run: Download done, status = %d",
                Status);
        OtaCount_Done++;
        OtaSignalEvent(OTA_EVENT_DOWNLOAD_DONE);
        break;

    case OTA_RUN_ERROR_CONSECUTIVE_OTA_ERRORS: /* 5 consecutive failures, must stop */
#ifdef OTA_LOOP_TESTING
        /* just for loop testing  - ignore CONSECUTIVE_OTA_ERRORS */
        Display_printf(display, 0, 0,"[OTA Task] OtaRunStep: ignore it just for loop testing");
        OtaSignalEvent(OTA_EVENT_RESTART);
        break;
#endif

    case OTA_RUN_ERROR_NO_SERVER_NO_VENDOR:
    case OTA_RUN_ERROR_UNEXPECTED_STATE:
    case OTA_RUN_ERROR_SECURITY_ALERT: /* security alert, must stop */
        OtaCount_Errors++;
        /* User could wait OTA period time and try maybe there is good new version to download */
        Display_printf(
                display,
                0,
                0,
                "[OTA Task] OtaRunStep: FATAL ERROR from Ota_run %d !!!!!!!!!!!!!!!!!!!!!!!!!!!",
                Status);
        OtaSignalEvent(OTA_EVENT_ERROR);
        break;

    default:
        if (Status < 0)
        {
            Display_printf(
                    display,
                    0,
                    0,
                    "[OTA Task] OtaRunStep: Unknown negative from Ota_run %d, halt!!",
                    Status);
            OtaSignalEvent(OTA_EVENT_ERROR);
        }
        else
        {
            Display_printf(
                    display,
                    0,
                    0,
                    "[OTA Task] OtaRunStep: Unknown positive status from Ota_run %d, continue",
                    Status);
            OtaSignalEvent(OTA_EVENT_CONTINUE);
        }
        break;
    }

    return 0;
}

int32_t OtaIsActive()
{
    int ProcActive;
    int ProcActiveLen;

    /* check OTA process */
    OTA_get(EXTLIB_OTA_GET_OPT_IS_ACTIVE, (int32_t *) &ProcActiveLen,
            (uint8_t *) &ProcActive);

    return ProcActive;            
}

int32_t OtaImageTestingAndReset()
{
    int32_t retVal = 0;

    Display_printf(display, 0, 0, "\n\n");
    Display_printf(display, 0, 0,
                   "[OTA Task] OtaImageTestingAndReset: download done");
    Display_printf(
            display,
            0,
            0,
            "[OTA Task] OtaImageTestingAndReset: call sl_Stop to move the bundle to testing state");

    sl_Stop(100);

    struct controlMsg controlMsg;

    Display_printf(display, 0, 0,
                   "[OTA Task] ProcessRestartMcu: reset the platform...");

    if (App_CB.controlQueue != NULL)
    {
        controlMsg.threadID = 2;
        controlMsg.msg = CONTROL_MSG_OTA_IMAGE_TEST;
        mq_send(App_CB.controlQueue, (char *) &controlMsg,
                sizeof(struct controlMsg), 0);
    }
    else
    {
        retVal = -1;
    }

    return retVal;
}

int16_t OtaSignalEvent(OtaEvent event)
{
    int32_t ret;
    struct timespec tm;

    /* signal provisioning task about SL Event */
    clock_gettime(CLOCK_REALTIME, &tm);
    tm.tv_sec = 0; /* do not wait */
    tm.tv_nsec = 0;
    ret = mq_timedsend(g_OtaSMQueue, (const char*) &event, 1, 0, &tm);

    if (ret < 0)
    {
        Display_printf(display, 0, 0,
                       "\t [OTA Task] Error - unable to send to msg queue");
        LOOP_FOREVER();
    }

    return 0;
}

static int32_t ReportError()
{
    OtaContext * const pCtx = &gOtaCtx;
    uint16_t eventIdx = 0;

    for (eventIdx = 0; eventIdx < OTA_EVENT_MAX; eventIdx++)
    {
        if (0 != (pCtx->pendingEvents & (1 << eventIdx)))
            break;
    }

    if (eventIdx < OTA_EVENT_MAX)
    {
        Display_printf(display, 0, 0,
                       "\t [OTA Task] Test failed: State = %d, Event = %d",
                       pCtx->currentState, eventIdx);
    }
    else
    {
        Display_printf(display, 0, 0,
                       "\t [OTA Task] Test failed: Unknown event");
    }
    return -1; /**/
}

void* OtaTask(void *pvParameters)
{
    int iRetVal = 0;
    int8_t event;
    OtaContext * const pCtx = &gOtaCtx;
    s_OtaTblEntry *pEntry = NULL;
    int32_t msgqRetVal;

    /* Queue management related configurations */
    /* Create the queue */
    mq_attr attr;
    attr.mq_maxmsg = 10; /* queue size */
    attr.mq_msgsize = sizeof(unsigned char); /* Size of message */
    g_OtaSMQueue = mq_open("ota msg q", O_CREAT, 0, &attr);

    if (iRetVal < 0)
    {
        Display_printf(display, 0, 0,
                       "\t [OTA Task] Error - unable to create the msg queue");
        LOOP_FOREVER();
    }

    /* Pend on semaphore gating task from running until main thread signals */
    sem_post(&App_CB.otaReady);
    sem_wait(&App_CB.startBarrier);

    Platform_TimerInit(OtaAsyncEvtTimerIntHandler, &gOtaAsyncEventTimer);
    StopAsyncEvtTimer();

    while (1)
    {
        msgqRetVal = mq_receive(g_OtaSMQueue, (char*) &event, 1, NULL);

        /* if message q is empty */
        if (msgqRetVal < 0)
        {
            OtaSignalEvent(OTA_EVENT_NULL);
            continue;
        }

        if (event != OTA_EVENT_NULL)
        {
            StopAsyncEvtTimer();
        }

        /* Find Next event entry */
        pEntry = (s_OtaTblEntry *) &gTransitionTable[pCtx->currentState][event];

        if (NULL != pEntry->p_evtHndl)
        {
            if (pEntry->p_evtHndl() < 0)
            {
                Display_printf(display, 0, 0,
                               "\t [OTA Task] Event handler failed..!!");
                LOOP_FOREVER();
            }
        }

        /* Change state according to event */
        if (pEntry->nextState != pCtx->currentState)
        {
            pCtx->currentState = pEntry->nextState;
        }
    }
}

static int32_t StartAsyncEvtTimer()
{
    OtaContext * const pCtx = &gOtaCtx;

    if (0 == pCtx->asyncEvtTimeout)
    {
        pCtx->asyncEvtTimeout = OTA_EVT_TIMEOUT;
    }

    Platform_TimerStart(pCtx->asyncEvtTimeout, gOtaAsyncEventTimer, 0);
    return 0;
}

static int32_t StopAsyncEvtTimer()
{
    OtaContext * const pCtx = &gOtaCtx;

    if (0 != pCtx->asyncEvtTimeout)
    {
        Platform_TimerStop(gOtaAsyncEventTimer);
        pCtx->asyncEvtTimeout = 0;
    }

    return 0;
}

void OtaAsyncEvtTimerIntHandler(sigval val)
{
    Platform_TimerInterruptClear();
    /* One Shot timer */
    StopAsyncEvtTimer();
    OtaSignalEvent(OTA_EVENT_TIMEOUT);
}

//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************
